using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Universe.Threading
{
    public class Lock
    {
        private ReaderWriterLock _rwl;


        private ReaderWriterLock Rwl
        {
            get
            {
                if (_rwl == null)
                    lock(this)
                        if (_rwl == null)
                            _rwl = new ReaderWriterLock();

                return _rwl;
            }
        }


        public IDisposable Reader
        {
            get
            {
                return new LockReader(this);
            }
        }

        public IDisposable Writer
        {
            get
            {
                return new LockWriter(this);
            }
        }

        class LockReader: IDisposable
        {
            private Lock _lock;
            private bool _isWrite;
            private bool _needDispose;


            public LockReader(Lock @lock)
            {
                _lock = @lock;
                _isWrite = _lock.Rwl.IsWriterLockHeld;
                if (_isWrite)
                    _lock.Rwl.AcquireWriterLock(Timeout.Infinite);
                else
                    _lock.Rwl.AcquireReaderLock(Timeout.Infinite);

                _needDispose = true;
            }


            void IDisposable.Dispose()
            {
                if (_needDispose)
                {
                    if (_isWrite)
                        _lock.Rwl.ReleaseWriterLock();
                    else
                        _lock.Rwl.ReleaseReaderLock();
                }
            }
        }

        class LockWriter : IDisposable
        {
            private Lock _lock;
            private bool _isUpgrade;
            private LockCookie _cookie;
            private bool _hasCookie;
            private bool _needDispose;

            public LockWriter(Lock @lock)
            {
                _lock = @lock;

                _isUpgrade = _lock.Rwl.IsReaderLockHeld;
                if (_isUpgrade)
                {
                    _cookie = _lock.Rwl.UpgradeToWriterLock(Timeout.Infinite);
                    _hasCookie = true;
                }
                else
                    _lock.Rwl.AcquireWriterLock(Timeout.Infinite);

                _needDispose = true;
            }

            void IDisposable.Dispose()
            {
                if (_needDispose)
                {
                    if (_isUpgrade)
                    {
                        if (_hasCookie)
                            _lock.Rwl.DowngradeFromWriterLock(ref _cookie);
                    }
                    else
                        _lock.Rwl.ReleaseWriterLock();
                }
            }
        }
    }
}
